# /usr/bin/env python
# -*-coding: utf-8 -*-
# Autor: @kr1shn4murt1 basado en: http://projects2009developer.files.wordpress.com/2009/03/scapy.pdf
# Referencias: http://tools.ietf.org/html/rfc2131 , http://tools.ietf.org/html/rfc2132
# Fecha: Septiembre 11 - 2013
# Dependencias: Ejecutarse con python 2.6.x o 2.7.x , tener scapy instalado en la maquina desde donde se ejecuta
# Este script actua como un servidor DHCP escuchando peticiones por la interfaz de red escogida, entrega la informacion relevante de direccionamiento ip
# a un cliente que la solicite, a mejorar esta el designar un rango de ips a entregar por ahora solo entrega 1 y agregar multihilo, luego de eso agregar
# otras funciones que sean las que ejecuten el ataque de dhcp spoofing

# Se importan las librearias de scapy

from scapy.all import *

# Se definen las variuables con los datos de la red
from scapy.layers.dhcp import DHCP, BOOTP
from scapy.layers.l2 import Ether

ip_Servidor = "192.168.1.1"
ip_cliente = "192.168.1.110"
mac_Servidor = "a0:a4:c5:1e:f4:1a"
mascara_Subred = "255.255.255.0"
puerta_Enlace = "192.168.1.113"


# interfaz_A_Sniffear = 'eth6'

# Se define una funcion que empieza a sniffear por la interfaz de red designada, se esniffean solo paquetes en los puertos 67 y 68 que son los que usa el servicio DHCP
def encontrar_Peticiones_Dchp():
    # De encontrar un paquetes que se este usando en ese puerto se procede a llamar otra funcion que es la que procesara los paquetes de acuerdo a su informacion
    sniff(filter='port 67 or port 68', prn=procesar_Peticiones_Dhcp)


# Esta funcion es la que procesa los paquetes teniendo en cuenta si son de tipo discover o request y de acuerdo a esto se crea y se envia un paquete como respuesta
# a dichas solicitudes
def procesar_Peticiones_Dhcp(paquete):
    # Se verifica que el paquete tenga la capa DHCP
    if paquete[DHCP]:
        # Se verifica si es un paquete tipo request DHCP message-type = discover (1)
        # para luego responder con un paquete tipo offer

        print('\tDetectado paquete DHCP tipo discover, se creara y enviara un paquete DHCP Offer como respuesta')

        if paquete[DHCP].options[0][1] == 1:
            for i in range(0, len(paquete[DHCP].options)):
                if paquete[DHCP].options[i][0] == "vendor_class_id":
                    if "DIXIN.DEV." in str(paquete[DHCP].options[i][1],'utf-8'):
                        print("The vendor_call_id(%s) is DIXIN device." % (str(paquete[DHCP].options[i][1])))
                    else:
                        print("The vendor_call_id(%s) is not DIXIN device ignore it."%(str(paquete[DHCP].options[i][1])))
                        return

            print('The client MAC is:', paquete[Ether].src)

            # Se crea el paquete DHCP Offer con la informacion requerida, el campo xid de la capa bootp se toma del paquete que lo solicita, ya que el paquete
            # respuesta debe tener el mismo numero
            capa_3_Ethernet = Ether(src=mac_Servidor, dst=paquete[Ether].src)
            capa_4_Ip = IP(src=ip_Servidor, dst=ip_cliente)
            capa_5_UDP = UDP(sport=67, dport=68)
            capa_6_BOOTP = BOOTP(op=2,
                                 yiaddr=ip_cliente,  # 服务端分配给客户端的IP
                                 siaddr=ip_Servidor,  # 下一个为客户端分配IP的服务器地址
                                 giaddr='0.0.0.0',  # DHCP中继IP地址
                                 sname="DIXIN.SERVER.DHCP",
                                 xid=paquete[BOOTP].xid)
            capa_7_DHCP = DHCP(
                options=[('message-type', 'offer'),
                         ('subnet_mask', '255.255.255.0'),
                         ('server_id', ip_Servidor),
                         ('lease_time', 1800),
                         ('domain', 'localdomain'),
                         ('name_server', ip_Servidor),
                         ('end')])

            # Se apilan las capas antes creadas con el separador '/' para crear el paquete DHCP tipo offer
            paquete_Offer = capa_3_Ethernet / capa_4_Ip / capa_5_UDP / capa_6_BOOTP / capa_7_DHCP

            # Se envia el paquete al cliente que lo solicito
            sendp(paquete_Offer)
            print('Paquete DHCP Offer enviado: ', paquete_Offer.summary())
        # Se verifica si es un paquete tipo request DHCP message-type = request (3)
        # para luego responder con un paquete tipo ACK
        if paquete[DHCP].options[0][1] == 3:
            print(
                '\tDetectado paquete DHCP tipo request, se creara y enviara un paquete DHCP ack como respuesta'
            )
            print('mac del cliente:', paquete[Ether].src)

            # Se crea el paquete ack, tiene los mismos datos que el paquete anterior solo que en la capa DHCP el campo message-type cambia el valor de offer por ack
            paquete_ACK = Ether(src=mac_Servidor, dst=paquete[Ether].src) / IP(
                src=ip_Servidor,
                dst=ip_cliente) / UDP(sport=67, dport=68) / BOOTP(
                op=2,
                yiaddr=ip_cliente,
                siaddr=ip_Servidor,
                giaddr='0.0.0.0',
                xid=paquete[BOOTP].xid) / DHCP(
                options=[('message-type', 'ack'),
                         ('subnet_mask', '255.255.255.0'),
                         ('server_id', ip_Servidor),
                         ('lease_time', 1800),
                         ('domain', 'localdomain'),
                         ('name_server', ip_Servidor),
                         ('end')])
            sendp(paquete_ACK)
            print('Paquete DHCP ACK enviado: ', paquete_ACK.summary())

    print('procesado', paquete.summary())


# Se llama a la funcion de sniffeo principal para iniciar la ejecucion del script

encontrar_Peticiones_Dchp()
